home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / fs / link.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  12KB  |  352 lines

  1. /* This file handles the LINK and UNLINK system calls.  It also deals with
  2.  * deallocating the storage used by a file when the last UNLINK is done to a
  3.  * file and the blocks must be returned to the free block pool.
  4.  *
  5.  * The entry points into this file are
  6.  *   do_link:    perform the LINK system call
  7.  *   do_unlink:    perform the UNLINK and RMDIR system calls
  8.  *   do_rename:    perform the RENAME system call
  9.  *   truncate:    release all the blocks associated with an inode
  10.  */
  11.  
  12. #include "fs.h"
  13. #include <sys/stat.h>
  14. #include <string.h>
  15. #include <minix/callnr.h>
  16. #include "buf.h"
  17. #include "file.h"
  18. #include "fproc.h"
  19. #include "inode.h"
  20. #include "param.h"
  21.  
  22. #define SAME 1000
  23. PRIVATE char dot2[NAME_MAX] =  "..\0\0\0\0\0\0\0\0\0\0\0";
  24.  
  25. /*===========================================================================*
  26.  *                do_link                         *
  27.  *===========================================================================*/
  28. PUBLIC int do_link()
  29. {
  30. /* Perform the link(name1, name2) system call. */
  31.  
  32.   register struct inode *ip, *rip;
  33.   register int r;
  34.   char string[NAME_MAX];
  35.   struct inode *new_ip;
  36.  
  37.   /* See if 'name' (file to be linked) exists. */
  38.   if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
  39.   if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
  40.  
  41.   /* Check to see if the file has maximum number of links already. */
  42.   r = OK;
  43.   if ( (rip->i_nlinks & BYTE) == LINK_MAX) r = EMLINK;
  44.  
  45.   /* Only super_user may link to directories. */
  46.   if (r == OK)
  47.     if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
  48.  
  49.   /* If error with 'name', return the inode. */
  50.   if (r != OK) {
  51.     put_inode(rip);
  52.     return(r);
  53.   }
  54.  
  55.   /* Does the final directory of 'name2' exist? */
  56.   if (fetch_name(name2, name2_length, M1) != OK) {
  57.     put_inode(rip);
  58.     return(err_code);
  59.   }
  60.   if ( (ip = last_dir(user_path, string)) == NIL_INODE) r = err_code;
  61.  
  62.   /* If 'name2' exists in full (even if no space) set 'r' to error. */
  63.   if (r == OK) {
  64.     if ( (new_ip = advance(ip, string)) == NIL_INODE) {
  65.         r = err_code;
  66.         if (r == ENOENT) r = OK;
  67.     } else {
  68.         put_inode(new_ip);
  69.         r = EEXIST;
  70.     }
  71.   }
  72.  
  73.   /* Check for links across devices. */
  74.   if (r == OK)
  75.     if (rip->i_dev != ip->i_dev) r = EXDEV;
  76.  
  77.   /* Try to link. */
  78.   if (r == OK)
  79.     r = search_dir(ip, string, &rip->i_num, ENTER);
  80.  
  81.   /* If success, register the linking. */
  82.   if (r == OK) {
  83.     rip->i_nlinks++;
  84.     rip->i_dirt = DIRTY;
  85.   }
  86.  
  87.   /* Done.  Release both inodes. */
  88.   put_inode(rip);
  89.   put_inode(ip);
  90.   return(r);
  91. }
  92.  
  93.  
  94. /*===========================================================================*
  95.  *                do_unlink                     *
  96.  *===========================================================================*/
  97. PUBLIC int do_unlink()
  98. {
  99. /* Perform the unlink(name) or rmdir(name) system call. The code for these two
  100.  * is almost the same.  They differ only in some condition testing.  Unlink()
  101.  * may be used by the superuser to do dangerous things; rmdir() may not.
  102.  */
  103.  
  104.   register struct inode *rip;
  105.   struct inode *rldirp;
  106.   register struct fproc *rfp;
  107.   int r, r1;
  108.   ino_t numb;
  109.   mode_t old_mode;
  110.   uid_t old_uid;
  111.   char string[NAME_MAX];
  112.  
  113.   /* Get the last directory in the path. */
  114.   if (fetch_name(name, name_length, M3) != OK) return(err_code);
  115.   if ( (rldirp = last_dir(user_path, string)) == NIL_INODE)
  116.     return(err_code);
  117.  
  118.   /* The last directory exists.  Does the file also exist? */
  119.   r = OK;
  120.   if ( (rip = advance(rldirp, string)) == NIL_INODE) r = err_code;
  121.  
  122.   /* If error, return inode. */
  123.   if (r != OK) {
  124.     put_inode(rldirp);
  125.     return(r);
  126.   }
  127.   old_mode = rip->i_mode;    /* save mode; it must be fudged for . and .. */
  128.   old_uid =  rip->i_uid;    /* save uid;  it must be fudged for . and .. */
  129.  
  130.   /* Now test if the call is allowed, separately for unlink() and rmdir(). */
  131.   if (fs_call == UNLINK) {
  132.     /* Only the su may unlink directories, but the su can unlink any dir.*/
  133.     if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
  134.  
  135.     /* Actually try to unlink the file; fails if parent is mode 0 etc. */
  136.     if (r == OK) r = search_dir(rldirp, string, (ino_t *) 0, DELETE);
  137.   } else {
  138.     /* The call is rmdir().  Five conditions have to met for this call:
  139.      *     - The file must be a directory
  140.      *    - The directory must be empty (except for . and ..)
  141.      *    - It must not be /
  142.       *    - The path must not end in . or ..
  143.      *    - The directory must not be anybody's working directory
  144.      */
  145.     if ( (rip->i_mode & I_TYPE) != I_DIRECTORY) r = ENOTDIR;
  146.     if (search_dir(rip, "", &numb, LOOK_UP) == OK) r = ENOTEMPTY;
  147.     if (strcmp(user_path, "/") == 0) r = EPERM;    /* can't remove root */
  148.     if (strcmp(string, ".") == 0 || strcmp(string, "..") == 0) r = EPERM;
  149.     for (rfp = &fproc[INIT_PROC_NR + 1]; rfp < &fproc[NR_PROCS]; rfp++) {
  150.         if (rfp->fp_workdir == rip || rfp->fp_rootdir == rip) {
  151.             r = EBUSY;    /* can't remove anybody's working dir*/
  152.             break;
  153.         }
  154.     }
  155.  
  156.     /* Actually try to unlink the file; fails if parent is mode 0 etc. */
  157.     if (r == OK) r = search_dir(rldirp, string, (ino_t *) 0, DELETE);
  158.  
  159.     /* If all the conditions have been met, remove . and .. from the dir.
  160.      * If the directory is not searchable, it will not be possible to
  161.       * unlink . and .. even though this is legal, so change the mode.
  162.      */
  163.     if (r == OK) {
  164.         rip->i_mode |= S_IRWXU;    /* turn on all the owner bits */
  165.         rip->i_uid = fp->fp_effuid;    /* may not fail due to uid */
  166.         if ( (r = search_dir(rip, ".",  (ino_t *) 0, DELETE)) == OK)
  167.             rip->i_nlinks --;    /* . pts to dir being removed*/
  168.         if ( (r1 = search_dir(rip, "..", (ino_t *) 0, DELETE)) == OK)
  169.             rldirp->i_nlinks--;    /* .. points to parent dir */
  170.         rip->i_dirt = DIRTY;
  171.         rldirp->i_dirt = DIRTY;
  172.         if (r1 != OK) r = r1;
  173.         rip->i_mode = old_mode;    /* restore the old mode */
  174.         rip->i_uid = old_uid;
  175.     }
  176.   }
  177.  
  178.   if (r == OK) {
  179.     rip->i_nlinks--;
  180.     rip->i_dirt = DIRTY;
  181.   }
  182.  
  183.   /* If unlink was possible, it has been done, otherwise it has not. */
  184.   rip->i_mode = old_mode;    /* restore mode in case it has been changed */
  185.   put_inode(rip);
  186.   put_inode(rldirp);
  187.   return(r);
  188. }
  189.  
  190.  
  191. /*===========================================================================*
  192.  *                do_rename                     *
  193.  *===========================================================================*/
  194. PUBLIC int do_rename()
  195. {
  196. /* Perform the rename(name1, name2) system call. */
  197.  
  198.   struct inode *old_dirp, *old_ip;    /* ptrs to old dir, file inodes */
  199.   struct inode *new_dirp, *new_ip;    /* ptrs to new dir, file inodes */
  200.   int r = OK;                /* error flag; initially no error */
  201.   int odir, ndir;            /* TRUE iff {old|new} file is dir */
  202.   char string[NAME_MAX+1], old_string[NAME_MAX+1];
  203.   char old_name[PATH_MAX+1];
  204.   ino_t numb;
  205.   
  206.   /* See if 'name1' (existing file) exists.  Get dir and file inodes. */
  207.   if (fetch_name(name1, name1_length, M1) != OK) return(err_code);
  208.   if ( (old_dirp = last_dir(user_path, string)) == NIL_INODE) return(err_code);
  209.  
  210.   if ( (old_ip = advance(old_dirp, string)) == NIL_INODE) r = err_code;
  211.   strcpy(old_name, user_path);    /* save the old name here */
  212.   strcpy(old_string, string);    /* save last component of the name here */
  213.  
  214.   /* See if 'name2' (new name) exists.  Get dir and file inodes. */
  215.   if (fetch_name(name2, name2_length, M1) != OK) r = err_code;
  216.   if ( (new_dirp = last_dir(user_path, string)) == NIL_INODE) r = err_code;
  217.   new_ip = advance(new_dirp, string);    /* not required to exist */
  218.  
  219.   /* If it is ok, check for a variety of possible errors. */
  220.   if (r == OK) {
  221.     /* The old path must not be a prefix of the new one. */
  222.     if (strncmp(old_name, user_path, strlen(old_name)) == 0) r = EINVAL;
  223.  
  224.     /* The old path must not be . or .. */
  225.     if (strcmp(old_name, ".")==0 || strcmp(old_name, "..")==0) r = EINVAL;
  226.  
  227.     /* Both directories must be on the same device. */
  228.     if (old_dirp->i_dev != new_dirp->i_dev) r = EXDEV;
  229.  
  230.     /* Both directories must be writable and searchable. */
  231.     if (forbidden(old_dirp, W_BIT | X_BIT, 0)) r = EACCES;
  232.     if (forbidden(new_dirp, W_BIT | X_BIT, 0)) r = EACCES;
  233.  
  234.     /* Some tests apply only if the new path exists. */
  235.     odir = S_ISDIR(old_ip->i_mode);    /* TRUE iff old file is dir */
  236.     if (new_ip != NIL_INODE) {
  237.         ndir = S_ISDIR(new_ip->i_mode);    /* TRUE iff new file is dir */
  238.         if (odir == TRUE && ndir == FALSE) r = ENOTDIR;
  239.         if (odir == FALSE && ndir == TRUE) r = EISDIR;
  240.         if (old_ip->i_num == new_ip->i_num)